﻿using System;
using OhioState.AssetManaged;
using OhioState.Graphics;
using OhioState.Graphics.Proxy;

namespace OhioState.Graphics.Proxy
{
    public class VolumeGradientHistogram : TexImage
    {
        private String filename;
        private String volumeName;

        public VolumeGradientHistogram(String name, String filename)
            : base(name)
        {
            this.volumeName = name;
            this.filename = filename;

            this.Width = 256;
            this.Height = 256;

            TexelFormat texelFormat = new TexelFormat();
            texelFormat.Format = ImageFormat.RGBA;
            texelFormat.Type = ByteFormat.BYTE;
            this.TexelType = texelFormat;
        }

        public override void GetTexture(out byte[] texels)
        {
            Volume volume = (Volume)AssetManager.Instance.Find(typeof(Volume),
                volumeName.Substring(0, volumeName.Length - "_grad_histogram".Length));
            if (volume == null)
            {
                texels = new byte[this.Width * this.Height * 4];
                return;
            }

            byte[] volumeValues;
            int xDim = 0, yDim = 0, zDim = 0;
            volumeValues = volume.GetTextureByte(out xDim, out yDim, out zDim);

            //make 512 or smth like that...
            //then change corresponding 256 below in the code; finally, make sure
            //to normalize somehow to get the proper values in the shader; for 
            //instance, just divide the max value of the gradient by 3 to keep it
            //below 256; then in the shader we have to multiply that value by 3
            //gradient cannot be larger than 443.40
            double[] bins = new double[256];
            //double[] bins = new double[512];

            //compute histogram here
            //int[,] result = new int[256, 256];
            int total_num_values = 0;
            if (volumeValues != null || volumeValues.Length > 0)
            {
                byte v000 = 0, v100 = 0, v010 = 0, v001 = 0;
                for (int k = 0; k < zDim; k++)
                {
                    for (int j = 0; j < yDim; j++)
                    {
                        for (int i = 0; i < xDim; i++)
                        {
                            if ((k + 1) * xDim * yDim + (j + 1) * xDim + i + 1 < xDim * yDim * zDim)
                            {
                                v000 = volumeValues[k * xDim * yDim + j * xDim + i];
                                v100 = volumeValues[k * xDim * yDim + j * xDim + i + 1];
                                v010 = volumeValues[k * xDim * yDim + (j + 1) * xDim + i];
                                //v110 = volumeValues[k * xDim * yDim + (j + 1) * xDim + i + 1];
                                v001 = volumeValues[(k + 1) * xDim * yDim + j * xDim + i];
                                //v101 = volumeValues[(k + 1) * xDim * yDim + j * xDim + i + 1];
                                //v011 = volumeValues[(k + 1) * xDim * yDim + (j + 1) * xDim + i];
                                //v111 = volumeValues[(k + 1) * xDim * yDim + (j + 1) * xDim + i + 1];
                                int gradient_x = v100 - v000;
                                int gradient_y = v010 - v000;
                                int gradient_z = v001 - v000;
                                int gradient_value = (int)System.Math.Sqrt(
                                    gradient_x * gradient_x + gradient_y * gradient_y + gradient_z * gradient_z);
                                if (gradient_value > 255) gradient_value = 255;//hack! should normalize instead
                                //result[v000, gradient_value]++;

                                bins[gradient_value]++;

                                total_num_values++;
                            }
                        }
                    }
                }

            }

            double[] cdf = new double[256];
            for (int i = 0; i < 256; i++)
            {
                for (int j = 0; j <= i; j++) //should it be <= instead of < ?
                {
                    cdf[i] += bins[j];
                }
            }
            double cdf_min = cdf[0];
            double cdf_max = cdf[0];
            for (int i = 0; i < cdf.Length; i++)
            {
                if (cdf_min > cdf[i]) cdf_min = cdf[i];
                if (cdf_max < cdf[i]) cdf_max = cdf[i];
            }

            int num_values = total_num_values;//volume.Width * volume.Height * volume.Depth;//number of voxels in a volume
            for (int i = 0; i < 256; i++)
            {
                cdf[i] = 255 * (cdf[i] - cdf_min) / (num_values - cdf_min);
            }

            texels = new byte[this.Width * this.Height * 4];
            for (int j = 0; j < 256; j++)
            {
                for (int i = 0; i < 256; i++)
                {
                    texels[4 * 256 * j + i * 4 + 0] = (byte)cdf[i];
                    texels[4 * 256 * j + i * 4 + 1] = (byte)cdf[i];
                    texels[4 * 256 * j + i * 4 + 2] = (byte)cdf[i];
                    texels[4 * 256 * j + i * 4 + 3] = (byte)cdf[i];
                }
            }
        }
    }
}
